package org.apache.hadoop.hdfs.protocol;

import java.io.IOException;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.ReconfigurationTaskStatus;
import org.apache.hadoop.hdfs.client.BlockReportOptions;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier;
import org.apache.hadoop.hdfs.security.token.block.BlockTokenSelector;
import org.apache.hadoop.security.KerberosInfo;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenInfo;

/**
 * ClientDatanodeProtocol定义了Client与Datanode之间的接口.
 *
 * ClientDatanodeProtocol中定义的接口可以分为两部分：
 *     一部分是支持HDFS文件读取操作的，例如getReplicaVisibleLength()以及getBlockLocalPathInfo()；另
 *     一部分是支持DFSAdmin中与数据节点管理相关的命令。
 */
@InterfaceAudience.Private
@InterfaceStability.Evolving
@KerberosInfo(serverPrincipal = DFSConfigKeys.DFS_DATANODE_KERBEROS_PRINCIPAL_KEY)
@TokenInfo(BlockTokenSelector.class)
public interface ClientDatanodeProtocol {
  public static final long versionID = 9L;

  /**
   * 客户端会调用getReplicaVisibleLength()方法从数据节点获取某个数据块副本真实的数据长度。
   *
   * 当客户端读取一个HDFS文件时，需要获取这个文件对应的所有数据块的长度，用于建立数据块的输入流，然后读取数据。
   * 但是Namenode元数据中文件的最后一个数据块长度与Datanode实际存储的可能不一致，
   * 所以客户端在创建输入流时就需要调用getReplicaVisibleLength()方法从Datanode获取这个数据块的真实长度。
   */
  long getReplicaVisibleLength(ExtendedBlock b) throws IOException;
  
  /**
   * 在用户管理员命令中有一个'hdfs dfsadmindatanodehost:port'命令，
   * 用于触发指定的 Datanode重新加载配置文件，
   * 停止服务那些已经从配置文件中删除的块池(blockPool)， 开始服务新添加的块池。
   **/
  void refreshNamenodes() throws IOException;

  /**
   * 在用户管理员命令中还有一个与块池管理相关的`hdfs dfsadmin-deleteBlockPool datanode-host:port blockpoolId [force]`命令，用于从指定Datanode删除blockpoolId对应的块池，
   * 如果force参数被设置了，那么无论这个块池目录中有没有数据都会被强制删除；否则，只有这个块池目录为空的情况下才会被删除。
   * 需要注意的是，如果Datanode还在服务这个块池，这个命令的执行将会失败。要停止一个数据节点服务指定的块池，需要调用上面提到的refreshNamenodes()方法。
   *
   * @param bpid  blockpoolId用于设置要被删除的块池ID
   * @param force 用于设置是否强制删除
   */
  void deleteBlockPool(String bpid, boolean force) throws IOException;
  
  /**
   * HDFS对于本地读取，也就是Client和保存该数据块的Datanode在同一台物理机器上时，是有很多优化的。
   * Client会调用ClientProtocol.getBlockLocalPathInfo()方法获取指定数据块文件以及数据块校验文件在当前节点上的本地路径，然后利用这个本地路径执行本地读取操作，
   * 而不是通过流式接口执行远程读取，这样也就大大优化了读取的性能。
   *
   * 在HDFS 2.6版本中，客户端会通过调用DataTransferProtocol接口从数据节点获取数据块文件的文件描述符，然后打开并读取文件以实现短路读操作，
   * 而不是通过ClientDatanodeProtoco接口。
   */
  BlockLocalPathInfo getBlockLocalPathInfo(ExtendedBlock block, Token<BlockTokenIdentifier> token) throws IOException;
  
  /**
   * 查询上一次触发的重新加载配置操作的运行情况
   */
  HdfsBlocksMetadata getHdfsBlocksMetadata(String blockPoolId, long []blockIds, List<Token<BlockTokenIdentifier>> tokens) throws IOException;

  /**
   * 关闭一个数据节点，这
   */
  void shutdownDatanode(boolean forUpgrade) throws IOException;  

  /**
   * getDatanodeInfo()方法用于获取指定Datanode的信息，
   * 这里的信息包括Datanode运行 的HDFS版本、Datanode配置的HDFS版本，
   * 以及Datanode的启动时间。对应于管理命令'hdfs dfsadmin-getDatanodeInfo'。
   */
  DatanodeLocalInfo getDatanodeInfo() throws IOException;

  /**
   * startReconfiguration()方法用于触发Datanode异步地从磁盘重新加载配置，并且应用该 配置。
   * 这个方法用于支持管理命令'hdfs dfsadmin-getDatanodeInfo-reconfigstart'。
   */
  void startReconfiguration() throws IOException;

  /**
   * 我们知道startReconfiguration()方法是异步地加载配置操作，所以HDFS提供了getReconfigurationStatus()方法用于查询上一次触发的重新加载配置操作的运行情况。
   */
  ReconfigurationTaskStatus getReconfigurationStatus() throws IOException;

  /**
   * Trigger a new block report.
   */
  void triggerBlockReport(BlockReportOptions options) throws IOException;
}
